Repository navigation
Configurable redirect URI and free loopback port for browser login - #125
Conversation
Each loopback listener served a single connection. With two listeners (127.0.0.1 and ::1), a browser's speculative connection or an earlier request could leave the login waiting forever, and a redirect carrying the identity platform's error (a declined consent, say) did too; on main both failed with "Failed to receive auth code from callback". Serve every connection on every listener until a request to the redirect URI's path carries this login's state, as RFC 8252 and MSAL do. An error redirect with that state fails the login with error and error_description (exit 3); other paths get 404, another state 400, and the wait gives up after 10 minutes. Responses close the connection. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
d8960cd to
d183726
Compare
|
Thanks @balcsida, this is a good change. Here's what I verified and what I added. Reproduced on main. I built throwaway binaries with the identity platform pointed at a local fake IdP and a script standing in for the browser:
Verified on this branch.
Found and fixed (
The receiver now follows RFC 8252 and MSAL practice:
Perplexity and the Entra redirect URI docs back this design. That meant replacing the PR's "accepts the callback on any path" with a path match, which works because Entra matches the path exactly anyway. Tests and docs. I added unit tests over real sockets: stray connection, favicon, wrong state, missing code, error redirect, and timeout. The black-box harness now passes all scenarios (normal, |
…ommands Bring the skills up to date with osodevops#119 and osodevops#125: - automation: listen binds 127.0.0.1, takes the subscription's clientState through TEAMS_CLI_CLIENT_STATE, and answers /lifecycle; subscribe create gets --lifecycle-url. The lifetimes now match Graph's table (Teams messages and chats up to three days, presence one hour, 45 minutes minimum, lifecycle URL over an hour), replacing "at most one hour", and say what to do on each lifecycle event. - setup: --redirect-uri for a bring-your-own app's browser login, what is saved to the profile, and what AADSTS50011, a "Sign-in failed" error and the 10-minute wait mean. agent_skill_commands_parse runs every teams line in the skills' code blocks through Cli::try_parse_from, so a renamed flag or a missing argument fails the build instead of teaching agents a broken command. AGENTS.md lists skills/ and adds it to the keep-in-sync rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
* feat(skills): add agent skills for setup, messaging, admin and automation Four installable SKILL.md files under skills/, following the osodevops skills convention (one focused skill per workflow surface, each with agents/openai.yaml): - teams-cli-setup: install, delegated sign-in, profiles, CI auth, the JSON envelope, exit codes and pagination - teams-cli-messaging: find ids, read, send, reply, react, delete and restore, attachments and inline images, channel files, chats - teams-cli-admin: teams, channels, members, tags, apps and tabs, with a confirmation procedure for destructive commands - teams-cli-automation: presence, meetings, activity notifications, change-notification subscriptions, the local listener, and polling Every command line in the skills was checked against the binary's --help output. README gets an "Agent Skills" section with the npx skills add install command; the docs index and CHANGELOG link to the skills, and the attachments spec names the real skill file. * feat(skills): cover listen hardening and --redirect-uri; test skill commands Bring the skills up to date with #119 and #125: - automation: listen binds 127.0.0.1, takes the subscription's clientState through TEAMS_CLI_CLIENT_STATE, and answers /lifecycle; subscribe create gets --lifecycle-url. The lifetimes now match Graph's table (Teams messages and chats up to three days, presence one hour, 45 minutes minimum, lifecycle URL over an hour), replacing "at most one hour", and say what to do on each lifecycle event. - setup: --redirect-uri for a bring-your-own app's browser login, what is saved to the profile, and what AADSTS50011, a "Sign-in failed" error and the 10-minute wait mean. agent_skill_commands_parse runs every teams line in the skills' code blocks through Cli::try_parse_from, so a renamed flag or a missing argument fails the build instead of teaching agents a broken command. AGENTS.md lists skills/ and adds it to the keep-in-sync rule. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Sion Smith <sion.smith@gmail.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The browser (authorization code + PKCE) login hardcoded the redirect URI
http://localhost:8400/callbackand listened only on127.0.0.1:8400. That ruled out public client apps whose registered redirect URI has no path, such as Microsoft's own "Microsoft Graph Command Line Tools" app (14d82eec-204b-4c2f-b7e8-296a70dab67e, registeredhttp://localhost), because the identity platform ignores the port of a localhost redirect URI but matches the path exactly. It also failed when port 8400 was busy, and missed the callback when the browser resolvedlocalhostto::1.Behaviour changes:
--redirect-uri,TEAMS_CLI_REDIRECT_URIand the profile'sredirect_uriselect the redirect URI forauth login,auth consent-urlandauth doctor, with the same precedence as the client and tenant IDs. A delegated login saves the flag value to the profile with the IDs; the env value is used but not saved.127.0.0.1and::1forlocalhost) and accepts the callback on any path. A URI must behttpand point atlocalhostor a loopback IP literal; anything else is invalid input (exit 2) before a browser opens. A port held by another process is still an error, since that process could receive the authorization code.http://localhost/callbackon a free port instead ofhttp://localhost:8400/callback, so a busy port 8400 no longer blocks login. Registrations withhttp://localhost:8400/callbackkeep working because the port is ignored.Usage:
Review guide:
27057f8the configurable URI:resolve_redirect_uriandremember_registrationinconfig.rs,loopback_addrsandbind_loopbackinauth_code_pkce.rs.d8960cdthe free port:bind_loopbacknow returns the shared port,redirect_uri_with_portrewrites the URI, and the authorize URL is built after binding.Testing: unit tests cover URI validation, the shared free port across listeners, the busy-port error and the URI rewrite; CLI tests cover the flag, env, profile and default for
consent-urlanddoctor, and that a bad URI exits 2 for both the browser and device code flows. Which URIs the two apps accept was checked read-only against the authorize endpoint withprompt=none(a registered URI gets alogin_requiredredirect, an unregistered one gets an inline error): the OSO app acceptshttp://localhost/callbackon any port, the Microsoft app acceptshttp://localhoston any port, and neither accepts127.0.0.1. No live login was run from this branch; the first real sign-in is the remaining check.